Инструкция по созданию серверного приложения на nodejs + koa

Koa - микрофреймфорк, разработанный командой разработчиков фреймворка Express, и призванный использовать самые последние стандарты языка JavaScript.
Koa “предлагает” активно использовать самые последние стандарты в языке, включая синтаксис async/await, который позволяет избавиться от ужасно нечитаемого “callback hell” и является хорошей альтернативой построения цепочек Promise. Углубляться в синтаксис здесь не будем, это не тема статьи.
Сам Koa, являясь микрофреймворком, из коробки содержит только middleware систему, в нем даже нет маршрутизации. Все это можно получить, подключив необходимую функциональность в middleware chain.

В данной статье рассмотрим создание самого приложения Koa, и рассмотрим популярные навесы на него, для расширения функциональности.

Требования

Koa требует версию Node не меньше 7.6.0.
Использовать более старые версии можно, если подключить Babel, но тогда немного теряется посыл самого Koa (см. выше).
Т.к. мы собираемся использовать достаточно новые фичи языка, то лучше не скупиться, и ставить последнюю стабильную версию Node.

Подготовка среды

Про Node.js

Если Node не установлен, то вам необходимо сюда. Если же у вас еще и Linux или macOS, можно установить через менеджер пакетов - здесь сказано, как.
Чтобы проверить, что с Node все в порядке, можно набрать команду:

1
$ node --version

Она выведет текущую версию Node.
Также и про NPM (Node Package Manager) не забудем:

1
$ npm --version

Если хочется NVM (Node Version Manager), то вы наверняка знаете, что это такое, и проблем поставить нужную версию Node.js у вас не составит.

Установка зависимостей

Создайте директорию проекта, где будет храниться весь код. Откройте терминал и перейдите в данную директорию.

Инициализируем среду пакета. Это, как минимум, поможет нам хранить список зависимостей рядом с нашим кодом, а как максимум - создать полноценный пакет для распространения в сообщество.

1
$ npm init

NPM задаст несколько вопросов. Отвечаем хотя бы на имя пакета и имя автора.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
$ npm init

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (ex) koa-simple-app
version: (1.0.0)
description: Koa simple application
entry point: (index.js)
test command:
git repository:
keywords:
author: Nariman Safiulin <woofilee@gmail.com>
license: (ISC)
About to write to <..>\package.json:

{
"name": "koa-simple-app",
"version": "1.0.0",
"description": "Koa simple application",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Nariman Safiulin <woofilee@gmail.com>",
"license": "ISC"
}


Is this ok? (yes)

У нас должен был появиться файл package.json.

Поставим koa:

1
$ npm i koa

Hello, World!

Создадим файл index.js, и заполним его слегка измененным стандартным примером Koa.

1
2
3
4
5
6
7
8
9
10
11
const Koa = require('koa');

const app = new Koa();

app.use(async ctx => {
ctx.body = 'Hello, World!';
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});

Код достаточно простой.
Мы создаем объект приложения. С помощью метода use мы цепляем новый middleware в цепочку. Все middleware будут выполняться по очереди с каждым запросом. В каждый middleware передаются два аргумента - ctx и next, где ctx - это, очевидно, контекст запроса, а next - следующая middleware в цепочке. В последнем middleware в цепочке next можно и не принимать (как в примере).
После чего мы говорим Koa слушать порт 3000. Небольшой callback позволяет нам понять, что сервер действительно запустился, а не завис.

Теперь можно запустить сервер:

1
$ node index.js

Если сервер успешно запустился, то можно зайти в браузер, и в адресной строке перейти по адресу localhost:3000. Должно отобразиться привычное всем Hello, World!.

Подронее про цепочки middleware, структуру контекста и события можно прочитать на сайте Koa.

С чем использовать

Альтернативы есть почти к каждому и ниже перечисленного. Их можно поискать в ссылках внизу статьи.

Koa-router

Библиотека позволяет получить необходимую функциональность роутинга запросов на разные обработчики. Без этого навеса навряд ли можно сделать что-то более менее серьезнее Hello, World!.
Кроме koa-router есть еще куча других аналогов, например более простой koa-route, которому сложно придумать какое-либо применение для грандиозных целей, и роутер с валидацией входящих и исходящих данных joi-router. Тем не менее, koa-router самый популярный на данный момент среди роутеров, и имеет сбалансированную функциональность, ничего лишнего.

1
$ npm i koa-router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Koa = require('koa');
const Router = require('koa-router'); // Изменения здесь

const app = new Koa();
const router = new Router(); // Инициализируем роутер

router.get('/', (ctx, next) => { // И здесь, мы явно принимаем GET запрос
ctx.body = 'Hello, World!';
});

app.use(router.routes()).use(router.allowedMethods()); // Здесь мы добавляем роуты в цепочку, а также разрешаем вводим OPTIONS запросы.

app.listen(3000, () => {
console.log('Server running on port 3000');
});

Подробнее про koa-router можно почитать здесь.

Koa-bodyparser

Koa из коробки, как мы уже выяснили, мало что умеет. Парсить сложные запросы тоже.
Если нам нужно принимать запросы с JSON телом, то этот навес необходим.

1
$ npm i koa-bodyparser
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const Koa = require('koa');
const BodyParser = require('koa-bodyparser'); // Изменения здесь

const app = new Koa();

app.use(bodyParser()); // Подключить парсер нужно до наших роутов

app.use(async ctx => {
// Распарсенный запрос будет лежать в ctx.request.body
// Если парсить нечего, будет пустой объект
ctx.body = ctx.request.body;
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});

Подробнее про koa-bodyparser можно почитать здесь.

Koa-logger

Логи того, что приходит, и что уходит, тоже нужны. Для разработки пойдет вариант koa-logger.
Выглядеть будет примерно так (взято из странички самого пакета):

1
2
3
4
5
6
7
8
<-- GET /
--> GET / 200 835ms 746b
<-- GET /
--> GET / 200 960ms 1.9kb
<-- GET /users
--> GET /users 200 357ms 922b
<-- GET /users?page=2
--> GET /users?page=2 200 466ms 4.66kb

Не густо. Если хочется совсем много информации, то можно попробовать bunyan-logger.

1
$ npm i koa-logger
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const Koa = require('koa');
const Logger = require('koa-logger'); // Изменения здесь

const app = new Koa();

app.use(Logger()); // Опять же, логгер раньше наших роутов

app.use(async ctx => {
ctx.body = 'Hello, World!';
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});

Репозиторий koa-logger здесь.

@koa/cors

CORS запросы иногда нужны, и это начинает доставлять головную боль, когда они недоступны.

1
$ npm i @koa/cors@2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const Koa = require('koa');
const CORS = require('@koa/cors'); // Изменения здесь

const app = new Koa();

app.use(CORS()); // Раньше роутов!

app.use(async ctx => {
ctx.body = 'Hello, World!';
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});

Репозиторий @koa/cors здесь.

Koa-compress

Сжатие ответов от сервера.

1
$ npm i koa-compress
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Koa = require('koa');
const Compress = require('koa-compress'); // Изменения здесь

const app = new Koa();

app.use(Compress({
filter: function (content_type) {
return /text/i.test(content_type)
},
threshold: 2048,
flush: require('zlib').Z_SYNC_FLUSH
})); // Раньше роутов! Здесь мы сжимаем любые текстовые ответы.

app.use(async ctx => {
ctx.body = 'Hello, World!';
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});

Подробнее про koa-compress здесь.

Koa-session

Если необходимы сессии на cookie, то это неплохой вариант.

1
$ npm i koa-session
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Koa = require('koa');
const Session = require('koa-session'); // Изменения здесь

const app = new Koa();

app.keys = ['secret key']; // По умолчанию, нам требуется какой-то набор байтов, с помощью которого можно подписать cookie сессию

app.use(Session(app)); // И здесь, все опции по умолчанию

app.use(async ctx => {
// Считаем, сколько раз мы вывели Hello, World!.
// Не забываем, что браузер еще запрашивает favicon, поэтому число может увеличиваться на 2.
let n = ctx.session.views || 0;
ctx.session.views = ++n;
ctx.body = `Hello, World! x${n}`;
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});

Подробнее про опции koa-session здесь.

Koa-JWT

Ну и, конечно, JWT. Очень полезны при разработке API. Быстрый пример к ним не привести, так что можно прочитать README в репозитории пакета.

1
$ npm i koa-jwt

Подробнее про koa-jwt здесь.

Вместо заключения

Koa, по сути, выполняет свою единственную важную функцию - это принимать запросы на сервер, и применять на них цепочку middleware. Это позволяет добиться хорошей гибкости в разработке приложения, и использовать только то, что необходимо.
Он также сильно похож на Express.
Так что проблем с ним возникнуть не должно.

Полезные ссылки